﻿using System.Collections;
using System.Collections.Generic;
using System;

namespace Utility.FSM
{
	public class State
	{
		Dictionary<string, State> nameSubstatesDict;

		public string Name { get; private set; }
		public string Fullname { get; private set; }
		public int Depth { get; private set; }
		public FSM FSM { get; private set; }
		public State UpperState { get; private set; }
		public bool IsRoot
		{
			get
			{
				return UpperState == null;
			}
		}

		IStateScript _script;
		public IStateScript Script
		{
			get
			{
				return _script;
			}
			set
			{
				if (_script != value)
				{
					_script = value;
					_script.OnInit (this);
				}
			}
		}

		public TScript GetScript<TScript> ()
			where TScript : IStateScript
		{
			return (TScript)_script;
		}

		public override string ToString ()
		{
			return Fullname;
		}

		public State (string name, FSM fsm, State upperState, IStateScript script = null)
		{
			Name = name;
			FSM = fsm;
			UpperState = upperState;
			Depth = 0;

			Script = script;

			//not a root
			if (upperState != null)
			{
				upperState.AddSubstate (this);
				if (upperState.IsRoot)
				{
					Fullname = Name;
				}
				else
				{
					Fullname = String.Concat (UpperState.Fullname, ".", Name);
				}
			}
			else
			{
				Fullname = Name;
			}
		}

		public State AddSubstate (string name, IStateScript script = null)
		{
			return FSM.AddNewState (name, this, script);
		}

		void AddSubstate (State substate)
		{
			if (nameSubstatesDict == null)
			{
				nameSubstatesDict = new Dictionary<string, State> ();
			}
			if (nameSubstatesDict.ContainsKey (substate.Name))
			{
				throw new Exception ("Repeated substate name: " + substate.Name);
			}
			else
			{
				nameSubstatesDict[substate.Name] = substate;
				substate.Depth = Depth + 1;
			}
		}

		public State GetSubstate (string name, bool throwException = false)
		{
			State ret;
			if (!nameSubstatesDict.TryGetValue (name, out ret))
			{
				ret = FSM.GetState (String.Concat (Fullname, ".", name));
			}
			if (ret == null && throwException)
			{
				throw new Exception ("Cannot find substate: " + name);
			}
			return ret;
		}

		public State GetSiblingState (string name, bool throwException = false)
		{
			if (string.IsNullOrEmpty (name))
			{
				throw new Exception ("Sibling state name is null");
			}

			State ret = null;
			if (!IsRoot)
			{
				ret = UpperState.GetSubstate (name);
			}
			if (ret == null && throwException)
			{
				throw new Exception ("Cannot find sibling: " + name);
			}
			return ret;
		}

		/// <summary>
		/// Note: a state is not ancestor of itself
		/// </summary>
		/// <param name="state"></param>
		/// <returns></returns>
		public bool IsAncestorOf (State state)
		{
			if (state == null)
			{
				return false;
			}

			if (state.FSM != FSM)
			{
				return false;
			}

			int d_depth = state.Depth - Depth;
			for (int i = 0; i < d_depth; i++)
			{
				state = state.UpperState;
				if (state == this)
				{
					return true;
				}
			}
			return false;
		}
	}
}